#ifndef __OBJ_FILE__
#define __OBJ_FILE__

#include "ko.h"

namespace k
{
	enum facc
	{
		OmQuery		= DAC_READ,
		OmRead		= DAC_READ,
		OmWrite		= DAC_WRITE,
		OmExecute	= DAC_EXEC,
	};
	enum modes 
	{
		OmOpen		= 0x10000000,		// open
		OmCreate	= 0x20000000,		// create
		OmTruncate	= 0x40000000,		// truncate
		OmAlways	= 0x80000000,		// always
		OmTemp		= 0x01000000,
		OmMapping	= 0x02000000,
	};

	struct NativeFile : Object, KiFileObject
	{
		fexp_begin fexp(KiDataObject) fexp(KiFileObject) fexp_end;

		typedef size_t mode_t;

		HANDLE	_file;

		NativeFile() : _file(0)
		{
			
		}

		~NativeFile()
		{
			close();
		}
		
		static uint32_t da( size_t acc )
		{
			uint32_t ret = FILE_READ_ATTRIBUTES;
			if( acc & OmExecute ) ret |= GENERIC_EXECUTE;
			if( acc & OmRead ) ret |= GENERIC_READ;
			if( acc & OmWrite ) ret |= GENERIC_WRITE;
			return ret;
		}

		static uint32_t oo( size_t mode )
		{
			size_t always = mode & OmAlways;
			size_t trunc = mode & OmTruncate;
			if( mode & OmOpen ) return always ? OPEN_ALWAYS : ( trunc ? TRUNCATE_EXISTING : OPEN_EXISTING);
			if( mode & OmCreate ) return always ? CREATE_ALWAYS : CREATE_NEW;
			return OPEN_EXISTING;
		}

		static uint32_t ff( size_t mode )
		{
			uint32_t ret = 0;
			if( mode & OmTemp ) ret |= FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE;
			if( mode & OmMapping ) ret |= FILE_FLAG_NO_BUFFERING;
			return ret;
		}

		virtual long close()
		{
			if( !_file || _file == INVALID_HANDLE_VALUE )
				return S_OK;
			CloseHandle( _file );
			_file = 0;
			return S_OK;
		};

		virtual long create( ccharp name, size_t acc, mode_t mode )
		{
			close();
			HANDLE h = ::CreateFileA( name, da(acc), 7, 0, oo(mode), ff(mode), 0 );
			if( h == INVALID_HANDLE_VALUE ) return E_FAIL;
			_file= h;
			set_access( acc );
			return S_OK;
		};
		
		virtual size_t get_dac()
		{
			return get_access();
		}

		virtual long readp( uint64_t pos, void * pbuf, size_t want, size_t * eff = 0 ) 
		{
			RFAILED( setptr( pos, SEEK_SET ) );
			return read( pbuf, want, eff );
		};
		virtual long writep( uint64_t pos, void * pbuf, size_t want, size_t * eff = 0 )
		{
			RFAILED( setptr( pos, SEEK_SET ) );
			return write( pbuf, want, eff );
		};

		virtual uint64_t length()
		{ 
			LARGE_INTEGER liFileSize;
			BOOL fok = GetFileSizeEx( _file, &liFileSize );
			if( !fok ) return -1;
			return liFileSize.QuadPart;
		};

		virtual long set_length( uint64_t len )
		{
			RFAILED( setptr( len, SEEK_SET ) );
			return truncate();
		};

		virtual long read( void * pbuf, size_t want, size_t * eff = 0 ) 
		{
			DWORD cb = want;
			BOOL fok = ReadFile( _file, pbuf, cb, &cb, 0 );
			if( !fok ) return E_FAIL;
			if( eff ) *eff = cb;
			return S_OK;
		};

		virtual long write( void * pbuf, size_t want, size_t * eff = 0 ) 
		{ 
			DWORD cb = want;
			BOOL fok = WriteFile( _file, pbuf, cb, &cb, 0 );
			if( !fok ) return E_FAIL;
			if( eff ) *eff = cb;
			return S_OK;
		};

		virtual long setptr( int64_t dist, size_t method ) 
		{ 
			LARGE_INTEGER liDist;
			liDist.QuadPart = dist;
			BOOL fok = SetFilePointerEx( _file, liDist, NULL, method );
			return fok ? S_OK : E_FAIL;
		};

		virtual uint64_t tell()
		{
			LARGE_INTEGER liFilePointer;
			LARGE_INTEGER liDist;
			liDist.QuadPart = 0;
			BOOL fok = SetFilePointerEx( _file, liDist, &liFilePointer, SEEK_SET );
			return fok ? liFilePointer.QuadPart : -1; 
		}

		virtual long truncate() 
		{
			return SetEndOfFile( _file ) ? S_OK : E_FAIL;
		};

	};

	namespace vm
	{
		// this is kernel file object, handle bind it directly
		// any KiDataObject can wrap by this;
		struct KernelFile : Object, KiFileObject
		{
			define_clsid(ObFile);
			iptr<KiDataObject>	_data;
			uint64_t			_ptr;
			KernelFile() : _ptr(0)
			{};
			virtual long open( Object * data, size_t acc )
			{
				_data = data;
				if( !_data ) return E_INVALIDARG;
				_ptr = 0;
				_access = acc;
				return S_OK;
			}
			virtual long readp( uint64_t pos, void * pbuf, size_t want, size_t * eff = 0 ) 
			{ 
				if( !_data ) return E_UNEXPECTED;
				size_t eff_ = 0;
				long lr = _data->readp( pos, pbuf, want, &eff_ );
				if( lr < 0 ) return lr;
				_ptr += eff_;
				return lr; 
			};
			virtual long writep( uint64_t pos, void * pbuf, size_t want, size_t * eff = 0 )
			{
				if( !_data ) return E_UNEXPECTED;
				if( get_access() & DAC_WRITE ) return E_ACCESSDENIED;
				size_t eff_ = 0;
				long lr = _data->writep( pos, pbuf, want, &eff_ );
				if( lr < 0 ) return lr;
				_ptr += eff_;
				return lr; 
			}
			virtual uint64_t length()
			{
				if( !_data ) return E_UNEXPECTED;
				return _data->length();
			}
			virtual long set_length( uint64_t len )
			{
				if( !_data ) return E_UNEXPECTED;
				if( get_access() & DAC_WRITE ) return E_ACCESSDENIED;
				return _data->set_length( len );
			};
			virtual long read( void * pbuf, size_t want, size_t * eff = 0 ) 
			{
				return readp( _ptr, pbuf, want, eff );
			};
			virtual long write( void * pbuf, size_t want, size_t * eff = 0 ) 
			{ 
				return writep( _ptr, pbuf, want, eff );
			};
			virtual long setptr( int64_t dist, size_t method )
			{
				if( !_data ) return E_UNEXPECTED;
				if( method == SEEK_SET )
				{
					_ptr = dist;
				}
				else if( method == SEEK_CUR )
				{
					_ptr += dist;
				}
				else if( method == SEEK_END )
				{
					uint64_t fs = _data->length();
					if( fs == (uint64_t)-1 ) return E_FAIL;
					_ptr = fs + dist;
				}
				return S_OK;
			};
			virtual uint64_t tell()
			{
				return _ptr;
			}
			virtual long truncate() 
			{ 
				if( !_data ) return E_UNEXPECTED;
				return _data->set_length( _ptr );
			};
		};

		// this is "physical file" linked in vos volume
		// all written data can be rollback.
		struct WkVolFile : NamedObject, KiDataObject
		{
		
		};
		// this is well known module file
		struct WkmSparseFile : NamedObject, KiDataObject
		{
			
		};
	};
};



#endif